/* main.cpp - entry point for BLACC (Burk Labs' Anachronistic Compiler Compiler)
 *
 * BLACC is designed to be a parser generator that I can easily bend
 * to whatever inconvenient need I have.
 */

#include "common.h"
#include "lex.h"
#include <stdio.h>
#include <thread>
#include <stack>

static TTokenizedFile   PrepareInputTokens(Lex* ThisLex, const char* GrammarFilename,
    TToken IncludeToken);
static unsigned int     HardwareConcurrency(void);

int main(int ArgCount, char** Args)
    {
    const char* GrammarFilename;
//    TToken      NullToken = {nullptr, 0, 0};

//    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    // parse command-line so we can get an input file
    Global::ParseArgs(ArgCount, Args);
    printf("# of threads = %u\n", HardwareConcurrency());

    unique_ptr<Lex> ThisLex(new Lex);

    GrammarFilename = Global::InputFilename();
    if(!GrammarFilename)
        Usage("Missing name of input file.\n");

    TTokenizedFile  GrammarFile =
        PrepareInputTokens(ThisLex.get(), Global::InputFilename(), TToken::Null);
    }


/* PrepareInputTokens() - Get to the point we have a set of input tokens ready to parse.
 *
 * This function will, if it encounters no errors, completely tokenize all of the
 * input of the designated input file, as well as all files it %include's.
 ****************************************************************************************/
static TTokenizedFile  PrepareInputTokens(Lex *ThisLex, const char* GrammarFilename,
    TToken IncludeToken)
    {
    int         TokenErrors;

    // read the input file
    TTokenizedFile GrammarFile = ThisLex->FileLoad(GrammarFilename, IncludeToken);

    TokenErrors = GrammarFile->Tokenize();
    if(TokenErrors > 0)
        {
        if(TokenErrors > 1)
            printf("%d error tokens in '%s'\n", TokenErrors, Global::InputFilename());
        }
    else
        {
        // scan token list for valid %include directives
        for(auto TokenIter=GrammarFile->Begin(); TokenIter != GrammarFile->End(); ++TokenIter)
            {
            if(TokenIter->Type == Lex::INCLUDE && TokenIter[1].Type == Lex::QUOTED)
                {
                unique_ptr<char> Filename(TokenIter[1].Unquote());

                if(!ThisLex->Loaded(Filename.get()))
                    ThisLex->FileLoad(Filename.get(), TokenIter[0]);
                }
            }
        }


    return GrammarFile;
    }

/* HardwareConcurrency() - How much parallelism does the hardware have?
 *
 * Due to a very painful bug in Visual C++, we use a wrapper function for
 * what should be a simple library call.
 */
static unsigned int HardwareConcurrency(void)
    {
    static unsigned int ThreadCount;

    if(ThreadCount == 0)
        {
#ifdef _MSC_VER         // compiler believes catch unreasonable (true if there were no bug)
#   pragma warning(push)
#   pragma warning(disable:4702)
#endif
        try {
            ThreadCount = std::thread::hardware_concurrency();
            }
        catch (...)
            {
            fprintf(stderr,
                "Can't happen: hardware_concurrency() threw an exception.\n"
                "Are you running under Windows/AppVerifier with HighVersionLie checked?\n"
                "If so, you may want to do something like:\n"
                "    appverif -disable HighVersionLie -for blacc.exe\n"
                );
            throw;
            }
#ifdef _MSC_VER
#   pragma warning(pop)
#endif
        // note that 0 is essentially "don't know"
        if(ThreadCount == 0)
            ThreadCount = 1;
        }

    return ThreadCount;
    }
